#include "CSingaporeBusListBox.h"
#include <aknsdrawutils.h>
#include <aknscontrolcontext.h>
#include "Binder.h"
#include "FunTrace.h"

template<class T, TInt (T::*F)()>
TInt MemFunDelegate(TAny* aPtr)
	{
	T* obj = static_cast<T*>(aPtr);
	return (*obj.*F)();
	}

CSingaporeBusListBox2::CSingaporeBusListBox2()
{
iMinFrameDelay = KMaxTInt64;
}

void CSingaporeBusListBox2::Draw(const TRect& aRect) const
{

TTime timeStart, timeEnd;
timeStart.UniversalTime();

TTime timeCurrentFrame;
timeCurrentFrame.HomeTime();

if(timeCurrentFrame.MicroSecondsFrom(iTimePreviousFrame).Int64() < iMinFrameDelay)
	iMinFrameDelay = timeCurrentFrame.MicroSecondsFrom(iTimePreviousFrame).Int64();
iTimePreviousFrame.HomeTime();

CAknDoubleGraphicStyleListBox::Draw(aRect);

{
timeEnd.UniversalTime();
if(timeEnd.MicroSecondsFrom(timeStart).Int64() > iMaxFrameTime)
	iMaxFrameTime = timeEnd.MicroSecondsFrom(timeStart).Int64();

CWindowGc& gc = *iItemDrawer->Gc();
const CFont* font = ((const CFont*)AknLayoutUtils::LayoutFontFromId(EAknLogicalFontPrimarySmallFont));
gc.UseFont(font);
gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
gc.SetBrushColor(KRgbBlack);
gc.SetPenStyle(CGraphicsContext::ESolidPen);
gc.SetPenColor(KRgbRed);
TRect rect(aRect.Center(), TSize(0, 0));
rect.Grow(150, 20);
TBuf<128> buf;
buf.Format(_L("%Ld, %Ld, %Ld, %d"),
		iMinFrameDelay,
		timeEnd.MicroSecondsFrom(timeStart).Int64(),
		iMaxFrameTime,
		iCountFrames);
gc.DrawText(
		buf,
		rect,
		(rect.Height() + font->AscentInPixels()) / 2,
		CGraphicsContext::ELeft,
		0
		);
}

	
/*		iLastTimeFrame = timeEnd.MicroSecondsFrom(timeStart).Int64()/1000;
	iMaxTimeFrame = Max(iMaxTimeFrame, iLastTimeFrame);
	iAccumulatedTimeFrame += iLastTimeFrame;*/
	iCountFrames++;

}


CSingaporeBusListBoxItemDrawer::CSingaporeBusListBoxItemDrawer(CSingaporeBusListBox* aListBox, scalp::CFunction< void(const CSingaporeBusListBoxItemDrawer*, TInt, const TRect&, TBool, TBool, TBool, TBool )>* aFun) : iPreviousCurrentItem(KErrNotFound), iScrollingOn(EFalse)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	iListBox = aListBox;
	iDrawItemFun = aFun;
	iRedrawTimer->CreateLocal();
	iRedrawTarget = new (ELeave) scalp::CActiveTarget(
			scalp::MakeFunctionL( scalp::Bind(&RTimer::Cancel, &iRedrawTimer.Get()) )
	);
	//iRedrawTarget->AppendRunL(
	//		scalp::MakeFunctionL( scalp::Bind(&CSingaporeBusListBoxItemDrawer::Redraw, this) )
	//);
	iRedrawTarget->AppendRunL(
			scalp::MakeFunctionL( scalp::Bind(&CSingaporeBusListBox::InvalidateCurrentItemL, aListBox) )
	);
	iGc = &CCoeEnv::Static()->SystemGc();
	SetGc(iGc);
	}

void CSingaporeBusListBoxItemDrawer::Redraw()
	{
	//iListBox->DrawDeferred();
	if(iListBox && iListBox->CurrentItemIndex() != KErrNotFound)
		{
		
#if !defined(__SERIES60_3X__) || defined(__S60_50__) // except for S60 3rd
		iListBox->RedrawItem( iListBox->CurrentItemIndex() );
#else
		iListBox->DrawItem( iListBox->CurrentItemIndex() );
#endif
		}
	}

void CSingaporeBusListBoxItemDrawer::SetIconArray(CAknIconArray* aIconArray)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	iIconArray = aIconArray;
	}
const CAknIconArray* CSingaporeBusListBoxItemDrawer::IconArray() const
{
TFunTrace funTrace(__PRETTY_FUNCTION__);
return iIconArray.Get();
}

CSingaporeBusListBox* CSingaporeBusListBoxItemDrawer::ListBox() const
{
	return iListBox;
}


void CSingaporeBusListBoxItemDrawer::DrawActualItem(TInt aIndex, const TRect& aActualItemRect,
		TBool aItemIsCurrent, TBool aViewIsEmphasized, TBool aViewIsDimmed, 
		TBool aItemIsSelected) const
		{
		
		if(aItemIsCurrent && iPreviousCurrentItem != aIndex)
			{
			iTimePreviousCurrentItemChange.HomeTime();
			iPreviousCurrentItem = aIndex;
			//iListBox->ResetDragging();
			}

		TTime startTime, endTime;
		startTime.HomeTime();
		
		(*iDrawItemFun)(this, aIndex, aActualItemRect,
				aItemIsCurrent, aViewIsEmphasized, aViewIsDimmed, 
				aItemIsSelected);
		
		endTime.HomeTime();
		
		if(1 == 0)
		{
		const CFont* font = ((const CFont*)AknLayoutUtils::LayoutFontFromId(EAknLogicalFontPrimarySmallFont));
		Gc()->UseFont(font);
		Gc()->SetBrushStyle(CGraphicsContext::ESolidBrush);
		Gc()->SetBrushColor(KRgbWhite);
		Gc()->SetPenStyle(CGraphicsContext::ESolidPen);
		Gc()->SetPenColor(KRgbRed);
		TRect rect(aActualItemRect.Center(), TSize(0, 0));
		rect.Grow(40, 10);
		TBuf<128> buf;
		buf.Format(_L("%Ld us"), endTime.MicroSecondsFrom(startTime).Int64());
		Gc()->DrawText(
				buf,
				rect,
				(rect.Height() + font->AscentInPixels()) / 2,
				CGraphicsContext::ECenter,
				0
				);
		}
		
		}

void CSingaporeBusListBoxItemDrawer::ScheduleRedrawL(TTimeIntervalMicroSeconds32 anInterval)
	{
	if(iListBox->iFlicking)
		return;
	iRedrawTarget->Cancel();
	iRedrawTimer->After(iRedrawTarget->iStatus, anInterval);
	iRedrawTarget->SetActive();
	}

const TTime& CSingaporeBusListBoxItemDrawer::TimePreviousCurrentItemChange() const
	{
	return iTimePreviousCurrentItemChange;
	}

CSingaporeBusListBoxModel::CSingaporeBusListBoxModel(CSingaporeBusListBox* aListBox, MListBoxModel* aListBoxModel) : iListBoxModel(aListBoxModel), iPrevNumberOfItems(-1), iListBox(aListBox)
	{
	
	}
TInt CSingaporeBusListBoxModel::NumberOfItems() const
	{
	return iListBoxModel->NumberOfItems();
	}

void CSingaporeBusListBox::InvalidateCurrentItemL()
	{
	const TInt kDisableMarquee = 0x0020; // not defined in S60 5th edition
	
    if ( !(iItemDrawer->Flags() & kDisableMarquee) && iView->ItemIsVisible( CurrentItemIndex() ) )
    	{
		TRect redrawRect( 
			iView->ItemPos( CurrentItemIndex() ), 
			iItemDrawer->ItemCellSize() );
		redrawRect.Intersection( iView->ViewRect() );
		Window().Invalidate( redrawRect );
    	}
	}

void CSingaporeBusListBox::ConstructL(scalp::CFunction< void(const CSingaporeBusListBoxItemDrawer*, TInt, const TRect&, TBool, TBool, TBool, TBool )>* aFun, MListBoxModel* aListBoxModel, const CCoeControl* aParent, TInt aFlags)
	{
	iDragging = EFalse;
	iParent = aParent;
	iDraggingTimer = CPeriodic::NewL(CActive::EPriorityStandard);
	CEikListBox::ConstructL(
			new (ELeave) CSingaporeBusListBoxModel(this, aListBoxModel),
			new (ELeave) CSingaporeBusListBoxItemDrawer(this, aFun),
			aParent, 
			aFlags);
	
	iMinFrameDelay = KMaxTInt64;
	
	}
	

CSingaporeBusListBoxItemDrawer* CSingaporeBusListBox::ItemDrawer() const
	{
	return (CSingaporeBusListBoxItemDrawer*)iItemDrawer;
	}

TInt CSingaporeBusListBox::DraggingCallback()
	{
	iDragging = EFalse;
	iAccumulatedDragging = TPoint(0,0);
	DrawDeferred();
	}

void CSingaporeBusListBox::ResetDragging()
	{
	iDragging = EFalse;
	iDraggingTimer->Cancel();
	iAccumulatedDragging = TPoint(0,0);
	}


void CSingaporeBusListBox::HandlePointerEventL(const TPointerEvent& aPointerEvent)
    {
    // Remember to call base class implementation
	if(iLastEvent.iType == TPointerEvent::EButton1Down)
		iPenDown= ETrue;
	else if(iLastEvent.iType == TPointerEvent::EButton1Up)
		iPenDown= EFalse;
		
    CEikListBox::HandlePointerEventL(aPointerEvent);
    return;
    
    
    switch(aPointerEvent.iType)
    	{
		case TPointerEvent::EButton1Down:
			{
			iDraggingTimer->Cancel();
			iDragOrigin = aPointerEvent.iPosition;
			if(!iDragging)
				iAccumulatedDragging = TPoint(0,0);
		    CEikListBox::HandlePointerEventL(aPointerEvent);
			}
			break;
		case TPointerEvent::EButton1Up:
			{
			if(iLastEvent.iType == TPointerEvent::EDrag)
				{
				iAccumulatedDragging += aPointerEvent.iPosition - iDragOrigin;
				iDraggingTimer->Cancel();
				iDraggingTimer->Start( TTimeIntervalMicroSeconds32(2000000), KMaxTInt, TCallBack(&MemFunDelegate<CSingaporeBusListBox, &CSingaporeBusListBox::DraggingCallback>, this) );
				}
			else
			    CEikListBox::HandlePointerEventL(aPointerEvent);
			}
			break;
		case TPointerEvent::EDrag:
			{
			iDragging = ETrue;
			iDraggingOffset = aPointerEvent.iPosition - iDragOrigin + iAccumulatedDragging;
			DrawDeferred();
		    CEikListBox::HandlePointerEventL(aPointerEvent);
			}
			break;
		default:
		    CEikListBox::HandlePointerEventL(aPointerEvent);
			break;
    	}
    
    iLastEvent = aPointerEvent;
    

    }

TBool CSingaporeBusListBox::Dragging() const
{
	return iDragging;
}
TPoint CSingaporeBusListBox::DraggingOffset() const
{
	return iDraggingOffset;
}

void CSingaporeBusListBox::ReportListBoxEventL( MEikListBoxObserver::TListBoxEvent aEvent )
	{
#ifndef __SERIES60_3X__
	const TInt kEventFlickStarted = MEikListBoxObserver::EEventFlickStarted;
	const TInt kEventFlickStopped = MEikListBoxObserver::EEventFlickStopped;
#else
	const TInt kEventFlickStarted = MEikListBoxObserver::EEventEnterKeyPressed + 10;
	const TInt kEventFlickStopped = kEventFlickStarted+1;
#endif
	if(aEvent == kEventFlickStarted)
		iFlicking = ETrue;
	else if(aEvent == kEventFlickStopped)
		{
		iFlicking = EFalse;
		DrawDeferred();
		}
	CEikListBox::ReportListBoxEventL(aEvent);
	}

void CSingaporeBusListBox::Draw(const TRect& aRect) const
	{
	
	/*
	User::Heap().AllocSize(iAllocMem);  // Amount allocated
	iHeapSize = User::Heap().Size();  // Current heap size
	iLimitHeapSize = User::Heap().MaxLength();  // Max heap size
	*/
	TTime timeStart, timeEnd;
	timeStart.UniversalTime();
	
	TTime timeCurrentFrame;
	timeCurrentFrame.HomeTime();
	
	if(timeCurrentFrame.MicroSecondsFrom(iTimePreviousFrame).Int64() < iMinFrameDelay)
		iMinFrameDelay = timeCurrentFrame.MicroSecondsFrom(iTimePreviousFrame).Int64();
	iTimePreviousFrame.HomeTime();
	
	if(Model()->NumberOfItems() == 0)
		{
		CWindowGc& gc = SystemGc();
		if(View()->EmptyListText())
			{
			AknDrawWithSkins::DrawEmptyList(
			        aRect,
			        gc,
			        *View()->EmptyListText(),
			        const_cast<CSingaporeBusListBox*>(this));
			}
		}
//#ifndef __SERIES60_3X__ // Symbian^3 does not define that macro
#if !defined(__SERIES60_3X__) || defined(__S60_50__) // except for S60 3rd
	else
		 {
			CEikListBox::Draw(aRect);
		 }
#else // Version before Symbian^3 require custom draw because they do not draw the skin if the items do not cover the entire screen
	 else
		 {

			const CListBoxView* view = View();
			TInt firstIndex = view->TopItemIndex();
			TInt lastIndex = view->BottomItemIndex();
			for (TInt i = firstIndex; i <= lastIndex; i++)
				view->DrawItem(i);
			CWindowGc& gc = SystemGc();
			TRect rect(
				0, Min(Model()->NumberOfItems(), Rect().Height() / ItemHeight()) * ItemHeight(),
				Rect().iBr.iX, Rect().iBr.iY
			);
			MAknsControlContext* controlContext = NULL;
			(const_cast<CSingaporeBusListBox*>(this))->MopGetObject(controlContext);
			if(controlContext)
			{
				AknsDrawUtils::Background(AknsUtils::SkinInstance(),
				controlContext, 
				this, 
				gc,
				rect
				);
			}
		 }
#endif

	if(1 == 0)
	{
	timeEnd.UniversalTime();
	if(timeEnd.MicroSecondsFrom(timeStart).Int64() > iMaxFrameTime)
		iMaxFrameTime = timeEnd.MicroSecondsFrom(timeStart).Int64();
	
	CWindowGc& gc = SystemGc();
	const CFont* font = ((const CFont*)AknLayoutUtils::LayoutFontFromId(EAknLogicalFontPrimarySmallFont));
	gc.UseFont(font);
	gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
	gc.SetBrushColor(KRgbBlack);
	gc.SetPenStyle(CGraphicsContext::ESolidPen);
	gc.SetPenColor(KRgbRed);
	TRect rect(aRect.Center(), TSize(0, 0));
	rect.Grow(150, 20);
	TBuf<128> buf;
	buf.Format(_L("%Ld, %Ld, %Ld, %d"),
			iMinFrameDelay,
			timeEnd.MicroSecondsFrom(timeStart).Int64(),
			iMaxFrameTime,
			iCountFrames);
	gc.DrawText(
			buf,
			rect,
			(rect.Height() + font->AscentInPixels()) / 2,
			CGraphicsContext::ECenter,
			0
			);
	}

		
/*		iLastTimeFrame = timeEnd.MicroSecondsFrom(timeStart).Int64()/1000;
		iMaxTimeFrame = Max(iMaxTimeFrame, iLastTimeFrame);
		iAccumulatedTimeFrame += iLastTimeFrame;*/
		iCountFrames++;
	
	}
